<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Program Listing</title>
    <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
    <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
    <link rel="start" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" />
    <link rel="up" href="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
    <link rel="prev" href="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
    <link rel="next" href="addfeatures.html" title="Chapter 6. Additional Features" />
  </head>
  <body>
    <div xmlns="" class="navheader">
      <div class="libver">
        <p>Library Version 12.1.6.2</p>
      </div>
      <table width="100%" summary="Navigation header">
        <tr>
          <th colspan="3" align="center">Program Listing</th>
        </tr>
        <tr>
          <td width="20%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
          <th width="60%" align="center">Chapter 5. Configuring for Write Forwarding</th>
          <td width="20%" align="right"> <a accesskey="n" href="addfeatures.html">Next</a></td>
        </tr>
      </table>
      <hr />
    </div>
    <div class="sect1" lang="en" xml:lang="en">
      <div class="titlepage">
        <div>
          <div>
            <h2 class="title" style="clear: both"><a id="wrfor_programlisting"></a>Program Listing</h2>
          </div>
        </div>
      </div>
      <div class="toc">
        <dl>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_main_c">Function: main()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_create_env_c">Function: create_env()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_env_init_c">Function: env_init()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#idm140264891973856">Function: doloop()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_printstocks_c">
                            <span>Function: print_stocks()</span>
                            
                                </a>
            </span>
          </dt>
        </dl>
      </div>
      <p> 
            Our example program builds from the simple transactional
            application you created in the <a class="xref" href="txnapp.html" title="Chapter 2. Transactional Application">Transactional Application</a> 
            chapter and configures write forwarding. The application is 
            network-aware, so you can specify things like host names and 
            ports from the command line. This program has additional error
            handling for replication errors. 
        </p>
      <p> 
            When using replication with write forwarding, there are several benefits for your application code: 
        </p>
      <div class="itemizedlist">
        <ul type="disc">
          <li>
            <p> 
                    You do not need to create an event handler to detect
                    changes of the master.
                </p>
          </li>
          <li>
            <p> 
                    You do not need to use app_data to track whether the
                    current site is master. 
                </p>
          </li>
          <li>
            <p>
                    You do not need to provide an error for put
                    operations on the client. 
                </p>
          </li>
        </ul>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_main_c"></a>Function: main()</h3>
            </div>
          </div>
        </div>
        <p> Our program begins with some additional include statements: </p>
        <pre class="programlisting">/*
 * File: rep_mgr_wrfor_gsg.c
 */
include &lt;stdlib.h&gt;
include &lt;string.h&gt;
include &lt;errno.h&gt;
ifdef _WIN32
include &lt;windows.h&gt;
define  sleep(s)        Sleep(1000 * (s))
else /* !_WIN32 */
include &lt;unistd.h&gt;
endif

#include &lt;db.h&gt; 

#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#endif  </pre>
        <p> 
                We then define a few values. One is the size of our cache,
                which we keep deliberately small for this example, and the other
                is the name of our database. Also, you can define a sleep
                time, which sets the time that a site waits before it retries
                synchronizing with the master. We also provide a global variable that
                is the name of our program; this is used for error reporting
                later on. 
            </p>
        <pre class="programlisting">#define CACHESIZE   (10 * 1024 * 1024)
#define DATABASE    "quote.db"
#define SLEEPTIME   3
const char *progname = "ex_rep_gsg_wrfor";  </pre>
        <p> 
                Then we perform a couple of forward declarations. The first
                of these, <code class="function">create_env()</code> and
                    <code class="function">env_init()</code> are used to open and
                initialize our environment. 
            </p>
        <p>
                Next we declare <code class="function">doloop()</code>. This function
                now takes an <code class="literal">int</code> as an argument, which we
                describe later. </p>
        <p> Finally, we have <code class="function">print_stocks</code> function. 
            </p>
        <pre class="programlisting">int create_env(const char *, DB_ENV **);
int env_init(DB_ENV *, const char *);
int doloop (DB_ENV *, int);
int print_stocks(DB *);  </pre>
        <p> 
                Next we need our <code class="function">usage()</code> function, which
                has additional options:
            </p>
        <pre class="programlisting">/* Usage function */
static void
usage()
  {
   fprintf(stderr, "usage: %s ", progname);
   fprintf(stderr, "-h home -l|-L host:port [-r host:port]\n");
   fprintf(stderr, "where:\n");
   fprintf(stderr, "\t-h identifies the environment home directory ");
   fprintf(stderr, "(required).\n");
   fprintf(stderr, "\t-l identifies the host and port used by this ");
   fprintf(stderr, "site (required unless L is specified).\n");
   fprintf(stderr, "\t-L identifies the local site as group creator. \n");
   fprintf(stderr, "\t-r identifies another site participating in "); 
   fprintf(stderr, "this replication group\n");
   exit(EXIT_FAILURE);
  }  </pre>
        <p> 
                where: 
            </p>
        <div class="itemizedlist">
          <ul type="disc">
            <li>
              <p>
                        <code class="literal">-h</code>
                    </p>
              <p> 
                        Identifies the environment home directory. You must
                        specify this option. 
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-l</code>
                    </p>
              <p>
                        Identifies the host and port used by this site. You
                        must specify this option unless <code class="literal">-L</code> is
                        specified.
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-L</code>
                    </p>
              <p> 
                        Identifies the local site as group creator. You must
                        specify this option unless <code class="literal">-l</code> is
                        specified.
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-r</code>
                    </p>
              <p>
                        Optionally identifies another site participating in
                        this replication group.
                    </p>
            </li>
          </ul>
        </div>
        <p> 
                That completed, we can jump into our application's
                    <code class="function">main()</code> function. We begin by declaring
                and initializing some variables used to collect
                TCP/IP host and port information. We also declare a couple of
                flags that we use to make sure some required information
                is provided to this application: 
            </p>
        <pre class="programlisting">int
main(int argc, char *argv[])
{
    extern char *optarg;
    DB_ENV *dbenv;
    const char *home;
    char ch, *host, *last_colon, *portstr;
    int is_group_creator, local_is_set, ret;
    u_int16_t port;

    dbenv = NULL;

    ret = is_group_creator = local_is_set = 0;
    home = NULL;  </pre>
        <p>
                Now we create and configure our environment handle. We do
                this with our <code class="function">create_env()</code> function, which
                we will show later in this example. 
            </p>
        <pre class="programlisting">    if ((ret = create_env(progname, &amp;dbenv)) != 0)
            goto err; </pre>
        <p> 
                Then we parse the command line arguments: 
            </p>
        <pre class="programlisting"> while ((ch = getopt(argc, argv, "h:l:L:r:")) != EOF)
    switch (ch) {
    case 'h':
        home = optarg;
        break;
    case 'L':
        is_group_creator = 1; /* FALLTHROUGH */
    case 'l':
        host = optarg;
        /*
         * The final colon in host:port string is the
         * boundary between the host and the port portions
         * of the string.
         */
        if ((last_colon = strrchr(host, ':')) == NULL ) {
        fprintf(stderr, "Bad local host specification.\n");
        goto err;
        }
        /*
         * Separate the host and port portions of the
         * string for further processing.
         */
        portstr = last_colon + 1;
        *last_colon = '\0';
        port = (unsigned short)atoi(portstr);
        if ((ret =
          dbenv-&gt;repmgr_site(dbenv, host, port, &amp;dbsite, 0)) != 0){
        fprintf(stderr, "Could not set local address %s:%d.\n",
          host, port);
        goto err;
        }
        dbsite-&gt;set_config(dbsite, DB_LOCAL_SITE, 1);
        if (is_group_creator)
        dbsite-&gt;set_config(dbsite, DB_GROUP_CREATOR, 1);

        if ((ret = dbsite-&gt;close(dbsite)) != 0) {
        dbenv-&gt;err(dbenv, ret, "DB_SITE-&gt;close");
        goto err;
             }
        local_is_set = 1;
        break;
    /* Identify another site in the replication group. */
    case 'r':
        host = optarg;
        /*
         * The final colon in host:port string is the
         * boundary between the host and the port portions
         * of the string.
         */
        if ((last_colon = strrchr(host, ':')) == NULL ) {
        fprintf(stderr, "Bad remote host specification.\n");
        goto err;
        }
        /*
         * Separate the host and port portions of the
         * string for further processing.
         */
        portstr = last_colon + 1;
        *last_colon = '\0';
        port = (unsigned short)atoi(portstr);
        if ((ret = 
        dbenv-&gt;repmgr_site(dbenv, host, port, &amp;dbsite, 0)) != 0) {
        dbenv-&gt;err(dbenv, ret, "DB_ENV-&gt;repmgr_site");
        goto err;
        }
        dbsite-&gt;set_config(dbsite, DB_BOOTSTRAP_HELPER, 1);
        if ((ret = dbsite-&gt;close(dbsite)) != 0) {
        dbenv-&gt;err(dbenv, ret, "DB_SITE-&gt;close");
        goto err;
        }
        break;
    case '?':
    default:
        usage();
    }  </pre>
        <p> 
                Now we can open our environment. We do this with the
                    <code class="function">env_init()</code> function which we will
                describe a little later in this chapter. 
            </p>
        <pre class="programlisting">    if ((ret = env_init(dbenv, home)) != 0)
            goto err; </pre>
        <p> 
                You can now configure and start Replication Manager with
                write forwarding. To configure write forwarding, use
                    <code class="function">rep_set_config</code> with the
                    <code class="literal">DB_REPMGR_CONF_FORWARD_WRITES</code> option.
            </p>
        <pre class="programlisting">    dbenv-&gt;rep_set_config(dbenv, DB_REPMGR_CONF_FORWARD_WRITES, 1); </pre>
        <pre class="programlisting">    if ((ret = dbenv-&gt;repmgr_start(dbenv, 3, DB_REP_ELECTION)) != 0)
            goto err; </pre>
        <p>
                Now that we have opened the environment and configured and
                started Replication Manager with write forwarding, we can call
                our <code class="function">doloop()</code> function. 
            </p>
        <pre class="programlisting">    if ((ret = doloop(dbenv, is_group_creator)) != 0) {
        dbenv-&gt;err(dbenv, ret, "Application failed");
        goto err;
    }  </pre>
        <p>
                Finally, we provide our application shutdown code. Note, 
                again, that in a traditional transactional application all 
                databases would be closed here. In our replicated application,
                the database will usually be closed in the <code class="function">doloop()</code>
                function, but we also conditionally close the database here to handle
                some error cases.
            </p>
        <pre class="programlisting">err: if (dbenv != NULL)
        (void)dbenv-&gt;close(dbenv, 0);

    return (ret);
}  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_create_env_c"></a>Function: create_env()</h3>
            </div>
          </div>
        </div>
        <p>
                Having written our <code class="function">main()</code> function, we
                now implement the usual <code class="function">create_env</code>
                function.
            </p>
        <pre class="programlisting">int
create_env(const char *progname, DB_ENV **dbenvp)
{
    DB_ENV *dbenv;
    int ret;

    if ((ret = db_env_create(&amp;dbenv, 0)) != 0) {
        fprintf(stderr, "can't create env handle: %s\n",
            db_strerror(ret));
        return (ret);
    }

    dbenv-&gt;set_errfile(dbenv, stderr);
    dbenv-&gt;set_errpfx(dbenv, progname);

    *dbenvp = dbenv;
    return (0);
}  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_env_init_c"></a>Function: env_init()</h3>
            </div>
          </div>
        </div>
        <p> 
                Now we implement the <code class="function">env_init</code> function,
                which also uses the <code class="literal">DB_INIT_REP</code> and 
                <code class="literal">DB_THREAD</code> flags that are required for 
                Replication Manager. 
            </p>
        <pre class="programlisting">int
env_init(DB_ENV *dbenv, const char *home)
{
    u_int32_t flags;
    int ret;

    (void)dbenv-&gt;set_cachesize(dbenv, 0, CACHESIZE, 0);
    (void)dbenv-&gt;set_flags(dbenv, DB_TXN_NOSYNC, 1);

    /* DB_INIT_REP and DB_THREAD are required for Replication Manager. */
    flags = DB_CREATE |
        DB_INIT_LOCK |
        DB_INIT_LOG |
        DB_INIT_MPOOL |
        DB_INIT_REP |
        DB_INIT_TXN |
        DB_RECOVER |
        DB_THREAD;

    if ((ret = dbenv-&gt;open(dbenv, home, flags, 0)) != 0)
        dbenv-&gt;err(dbenv, ret, "can't open environment");
    return (ret);
}  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="idm140264891973856"></a>Function: doloop()</h3>
            </div>
          </div>
        </div>
        <p> 
                Having written our <code class="function">main()</code> function and
                utility functions, we now implement <code class="function">doloop</code>.
                This function provides a command prompt at which the user can
                enter a stock ticker value and a price for that value. This
                information is then entered to the database. 
            </p>
        <p>
                To display the database, simply enter <code class="literal">return</code>
                at the prompt.
            </p>
        <p>
                To begin, we declare a database pointer, several
                    <code class="classname">DBT</code> variables, and the usual
                assortment of variables used for buffers and return codes. We
                also initialize all of this. Remember that doloop now takes
                <code class="literal">is_group_creator</code> as an additional
                argument.
            </p>
        <pre class="programlisting">#define BUFSIZE 1024
int
doloop(DB_ENV *dbenv, int is_group_creator)
{
    DB *dbp;
    DBT key, data;
    char buf[BUFSIZE], *rbuf;
    int ret;
    u_int32_t db_flags;

    dbp = NULL;
    memset(&amp;key, 0, sizeof(key));
    memset(&amp;data, 0, sizeof(data));
    ret = 0;  </pre>
        <p>
                Next, we begin the loop and we immediately open our database
                if it has not already been opened.
            </p>
        <p> 
                If <code class="literal">-L</code> is set,
                    <code class="literal">is_group_creator</code> specifies the need to
                create the database for the initial group creator startup. The
                database will be replicated to the other sites when they first
                start up. The database will already exist on each site for
                subsequent startups.
            </p>
        <p>
                Note that there is some logic for a site to retry in case it
                needs time to synchronize with the master using
                    <code class="literal">SLEEPTIME</code>.
            </p>
        <pre class="programlisting">    for (;;) {
    
        if (dbp == NULL) {
            if ((ret = db_create(&amp;dbp, dbenv, 0)) != 0)
                return (ret);

            db_flags = DB_AUTO_COMMIT;
            /*
             * Only need to create the database for the 
             * initial group creator startup.  The database 
             * will be replicated to the other sites when they
             * first start up.  The database will already exist on
             * each site for subsequent startups.
             */
            if (is_group_creator)
                db_flags |= DB_CREATE;
      
            if ((ret = dbp-&gt;open(dbp, NULL, DATABASE, 
                NULL, DB_BTREE, db_flags, 0)) != 0) {
            /* Retry in case site needs time to synchronize with master. */
            if (ret == ENOENT) {
                printf(
                  "No stock database yet available.\n");
                if ((ret = dbp-&gt;close(dbp, 0)) != 0) {
                    dbenv-&gt;err(dbenv, ret, "DB-&gt;close");
                    goto err;
                }
                dbp = NULL;
                sleep(SLEEPTIME);
                continue;
            }
            dbenv-&gt;err(dbenv, ret, "DB-&gt;open");
            goto err;
        }  </pre>
        <p> 
                Now we implement our command prompt. If the user
                enters the keywords <code class="literal">exit</code> or
                    <code class="literal">quit</code>, the loop is exited and the
                application ends. If the user enters nothing and instead simply
                presses <code class="literal">return</code>, the entire contents of the
                database is displayed. We use our
                    <code class="function">print_stocks()</code> function to display the
                database. (That implementation is shown next in this chapter.) 
            </p>
        <p>
                We also now check for a dead replication handle, which can occur
                in rare cases when a new master causes a previously committed 
                transaction to be rolled back. In such cases, all database handles
                must be closed and opened again.
            </p>
        <p> 
                Remember that very little error checking is performed on the
                data entered at this prompt. If the user fails to enter at least
                one space in the value string, a simple help message is printed
                and the prompt is returned to the user.
            </p>
        <pre class="programlisting">        printf("QUOTESERVER&gt; ");
        fflush(stdout);

        if (fgets(buf, sizeof(buf), stdin) == NULL)
            break;
        if (strtok(&amp;buf[0], " \t\n") == NULL) {
            switch ((ret = print_stocks(dbp))) {
            case 0:
                continue;
            case DB_REP_HANDLE_DEAD:
                /* Must close and reopen the handle, then can retry. */
                (void)dbp-&gt;close(dbp, 0);
                dbp = NULL;
                dbenv-&gt;errx(dbenv, "Could not traverse data, 
                                                   retry operation");
                continue;       
            default:
                dbp-&gt;err(dbp, ret, "Error traversing data");
                goto err;
            }
        }
        rbuf = strtok(NULL, " \t\n");
        if (rbuf == NULL || rbuf[0] == '\0') {
            if (strncmp(buf, "exit", 4) == 0 ||
                strncmp(buf, "quit", 4) == 0)
                break;
            dbenv-&gt;errx(dbenv, "Format: TICKER VALUE");
            continue;
        }  </pre>
        <p> 
                Now we assign data to the <code class="classname">DBT</code>s that we
                will use to write the new information to the database. 
            </p>
        <pre class="programlisting">        key.data = buf;
        key.size = (u_int32_t)strlen(buf);

        data.data = rbuf;
        data.size = (u_int32_t)strlen(rbuf);  </pre>
        <p>
                Having done that, we can write the new information to the
                database. Here, the reason we do not need an explicit commit 
                on this put operation is that it uses the implicit NULL txnid, 
                so each one is automatically committed. Also, the application
                retries if a deadlock, timeout or permission error occurs. A forwarded
                put operation can return a timeout error if the operation takes
                too long and a permission error if there is currently
                no master.
            </p>
        <pre class="programlisting">    if ((ret = dbp-&gt;put(dbp, NULL, &amp;key, &amp;data, 0)) != 0)
    {
        dbp-&gt;err(dbp, ret, "DB-&gt;put");
        switch (ret) {
        case DB_REP_HANDLE_DEAD:
            /* Must close and reopen the handle, then can retry. */
            (void)dbp-&gt;close(dbp, 0);
            dbp = NULL;
            /* FALLTHROUGH */
        case DB_LOCK_DEADLOCK:
        case DB_TIMEOUT:
        case EACCES:
            dbenv-&gt;errx(dbenv, "Could not update data, retry operation");
            continue;
        default:
            dbp-&gt;err(dbp, ret, "Error updating data");
            goto err;
        }
    }  </pre>
        <p> 
                Finally, we close our database before returning from the
                function.
            </p>
        <pre class="programlisting">err:    if (dbp != NULL)
        (void)dbp-&gt;close(dbp, 0);

    return (ret);
}  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_printstocks_c"></a>
                            <span>Function: print_stocks()</span>
                            
                                </h3>
            </div>
          </div>
        </div>
        <p>
               This function is unmodified from when we
               originally introduced it. For details on that function, see
               <a class="xref" href="simpleprogramlisting.html#printstocks_c" title="Function: print_stocks()">
                            <span>Function: print_stocks()</span>
                            
                            
                    </a>.
            </p>
      </div>
    </div>
    <div class="navfooter">
      <hr />
      <table width="100%" summary="Navigation footer">
        <tr>
          <td width="40%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
          <td width="20%" align="center">
            <a accesskey="u" href="writeforwarding.html">Up</a>
          </td>
          <td width="40%" align="right"> <a accesskey="n" href="addfeatures.html">Next</a></td>
        </tr>
        <tr>
          <td width="40%" align="left" valign="top">Chapter 5. Configuring for Write Forwarding </td>
          <td width="20%" align="center">
            <a accesskey="h" href="index.html">Home</a>
          </td>
          <td width="40%" align="right" valign="top"> Chapter 6. Additional Features</td>
        </tr>
      </table>
    </div>
  </body>
</html>
